import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.budo.mongo.filter.MongoFilter;
import org.budo.mongo.filter.MongoFilterChain;
import org.budo.mongo.filter.MongoFilterChainImpl;
import org.budo.mongo.wrapper.DBCollectionWrapper;
import org.budo.mongo.wrapper.DBCursorWrapper;
import org.budo.mongo.wrapper.DBWrapper;
import org.budo.mongo.wrapper.MongoCollectionWrapper;
import org.budo.mongo.wrapper.MongoDatabaseWrapper;
import org.budo.support.lang.util.StringUtil;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;

import lombok.Getter;
import lombok.Setter;

public class AbstractWrapper {
    WrapperType mongoCollectionWrapper = new WrapperType("org.budo.mongo.wrapper", // 
            "@SuppressWarnings(\"rawtypes\")\n" //
                    + "public class MongoCollectionWrapper implements MongoCollection {\n" // 
                    + "    private MongoFilterChain filterChain;\n\n" //
                    + "    private MongoCollection mongoCollection;\n\n" //
                    + "    public MongoCollectionWrapper(List<MongoFilter> filters, MongoCollection mongoCollection) {\n" //
                    + "        this.filterChain = new MongoFilterChainImpl(filters);\n" // 
                    + "        this.mongoCollection = mongoCollection;\n" // 
                    + "    }\n\n") //
                            ._import_0(MongoFilterChainImpl.class) // 
                            ._import_0(MongoFilter.class) // 
                            ._import_0(MongoFilterChain.class);

    WrapperType dbObjectWrapper = new WrapperType("org.budo.mongo.wrapper", // 
            "@SuppressWarnings({ \"rawtypes\", \"unchecked\", \"serial\" })\n" // 
                    + "public class DBObjectWrapper extends BasicDBObject {\n" // 
                    + "    private MongoFilterChain filterChain;\n\n" //
                    + "    private DBObject dbObject;\n\n" //
                    + "    public DBObjectWrapper(List<MongoFilter> filters, DBObject dbObject) {\n" //
                    + "        this.filterChain = new MongoFilterChainImpl(filters);\n" // 
                    + "        this.dbObject = dbObject;\n" // 
                    + "    }\n\n")//
                            ._import_0(DBObject.class) // 
                            ._import_0(BasicDBObject.class) // 
                            ._import_0(MongoFilter.class) // 
                            ._import_0(MongoFilterChainImpl.class) // 
                            ._import_0(MongoFilterChain.class);

    WrapperType dbCursorWrapper = new WrapperType("org.budo.mongo.wrapper", // 
            "@SuppressWarnings(\"deprecation\")\n" //
                    + "public class DBCursorWrapper extends DBCursor {\n" // 
                    + "    private MongoFilterChain filterChain;\n\n" //
                    + "    private DBCursor dbCursor;\n\n" //
                    + "    public DBCursorWrapper(List<MongoFilter> filters, DBCursor dbCursor, DBCollection collection, DBObject query, DBObject fields, ReadPreference readPreference) {\n" //
                    + "        super(collection, query, fields, readPreference);\n" //
                    + "        this.filterChain = new MongoFilterChainImpl(filters);\n" // 
                    + "        this.dbCursor = dbCursor;\n" // 
                    + "    }\n\n")//
                            ._import_0(MongoFilterChainImpl.class) // 
                            ._import_0(MongoFilter.class) // 
                            ._import_0(MongoFilterChain.class);

    WrapperType dbCollectionWrapper = new WrapperType("org.budo.mongo.wrapper", "" //
            + "@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n" //
            + "public class DBCollectionWrapper extends DBCollection {\n" //
            + "    private DBCollection dbCollection;\n\n" //
            + "    private MongoFilterChain filterChain;\n\n" //
            + "    public DBCollectionWrapper(List<MongoFilter> filters, DBCollection dbCollection, DB db, String name) {\n" //
            + "        super(db, name);\r\n" //
            + "        this.filterChain = new MongoFilterChainImpl(filters);\n" // 
            + "        this.dbCollection = dbCollection;\n" //
            + "    }\n\n")//
                    ._import_0(MongoFilterChainImpl.class) // 
                    ._import_0(MongoFilter.class) // 
                    ._import_0(MongoFilterChain.class);

    WrapperType dbWrapper = new WrapperType("org.budo.mongo.wrapper", "" //
            + "public class DBWrapper extends DB {\n" //
            + "    private DB db;\n\n" //
            + "    private MongoFilterChain filterChain;\n\n" //
            + "    public DBWrapper(List<MongoFilter> filters, DB db, Mongo mongo, String name) {\n" //
            + "        super(mongo, name);\n" //
            + "        this.filterChain = new MongoFilterChainImpl(filters);\n" //
            + "        this.db = db;\n" //
            + "    }\n\n") //
                    ._import_0(MongoFilterChainImpl.class) // 
                    ._import_0(MongoFilter.class) // 
                    ._import_0(MongoFilterChain.class);

    WrapperType mongoDatabaseWrapper = new WrapperType("org.budo.mongo.wrapper", "" //
            + "public class MongoDatabaseWrapper implements MongoDatabase {\n" //
            + "    private MongoFilterChain filterChain;\n\n" //
            + "    private MongoDatabase mongoDatabase;\n\n" //
            + "    public MongoDatabaseWrapper(List<MongoFilter> filters, MongoDatabase mongoDatabase) {\n" //
            + "        this.filterChain = new MongoFilterChainImpl(filters);\n" //
            + "        this.mongoDatabase = mongoDatabase;\n" //
            + "    }\n\n") //
                    ._import_0(MongoFilterChainImpl.class) // 
                    ._import_0(MongoFilter.class) // 
                    ._import_0(MongoFilterChain.class) //
                    ._import_0(MongoDatabase.class);

    WrapperType mongoClientWrapper = new WrapperType("org.budo.mongo.wrapper", "" //
            + "@SuppressWarnings({ \"deprecation\", \"unchecked\", \"rawtypes\" })\n" //
            + "public class MongoClientWrapper extends MongoClient {\n" //
            + "    private MongoClient mongoClient;\n\n" //
            + "    private MongoFilterChain filterChain;\n\n" //
            + "    public MongoClientWrapper(List<MongoFilter> filters, MongoClient mongoClient) {\n" //
            + "        this.filterChain = new MongoFilterChainImpl(filters);\n"//
            + "        this.mongoClient = mongoClient;\n"//
            + "    }\n\n") //
                    ._import_0(MongoClient.class) //
                    ._import_0(MongoFilter.class) //
                    ._import_0(MongoFilter.class) // 
                    ._import_0(MongoFilterChain.class) //
                    ._import_0(MongoFilterChainImpl.class);

    WrapperType filterChain = new WrapperType("org.budo.mongo.filter", "" //
            + "@SuppressWarnings({ \"rawtypes\", \"deprecation\"})\n" //
            + "public interface MongoFilterChain {\n" //
            + "    void setFilters(List<MongoFilter> filters);\n\n" //
            + "    List<MongoFilter> getFilters();\n\n" + "    Integer getPosition();\n\n") //
                    ._import_0(MongoClient.class);

    WrapperType filterChainImpl = new WrapperType("org.budo.mongo.filter", "" //
            + "@Getter\n" //
            + "@Setter\n" //
            + "@SuppressWarnings({ \"rawtypes\", \"unchecked\", \"deprecation\" })\n"//
            + "public class MongoFilterChainImpl implements MongoFilterChain {\n" //
            + "    private List<MongoFilter> filters;\n\n" //
            + "    private Integer position;\n\n" //
            + "     public MongoFilterChainImpl(List<MongoFilter> filters) {\r\n" //
            + "        this.filters = filters;\r\n" //
            + "        this.position = 0;\r\n" //
            + "    }\n\n") //
                    ._import_0(MongoFilterChain.class) //
                    ._import_0(MongoClient.class) //
                    ._import_0(DB.class) //
                    ._import_0(MongoDatabaseWrapper.class) //
                    ._import_0(DBCollectionWrapper.class) //
                    ._import_0(DBCursorWrapper.class) //
                    ._import_0(MongoCollectionWrapper.class) //
                    ._import_0(DBWrapper.class) //
                    ._import_0(Setter.class) //
                    ._import_0(Getter.class);

    WrapperType mongoFilter = new WrapperType("org.budo.mongo.filter", //
            "@SuppressWarnings({\"rawtypes\", \"deprecation\"})\n" // 
                    + "public interface MongoFilter {\n") //
                            ._import_0(MongoClient.class);

    WrapperType abstractMongoFilter = new WrapperType("org.budo.mongo.filter", //
            "@SuppressWarnings({\"rawtypes\", \"deprecation\"})\n" // 
                    + "public class AbstractMongoFilter implements MongoFilter {\n") //
                            ._import_0(MongoClient.class);

    protected String _return(Method method) {
        String returnType = method.getReturnType().getSimpleName();
        if ("void" != returnType) {
            return "        return ";
        } else {
            return "        ";
        }
    }

    protected String wrapperResult_2(Method method, String _this) {
        String position_0 = "        this.position = 0;\n";
        String _result = _this + "." + method.getName() + "(" + args(method) + ")";

        if (method.getReturnType().equals(DBCollection.class)) {
            return position_0 // 
                    + "            DBCollection _dbCollection = " + _result + ";\n" // 
                    + "            return new DBCollectionWrapper(this.getFilters(), _dbCollection, _dbCollection.getDB(), _dbCollection.getName());\n";
        }

        if (method.getReturnType().equals(DB.class)) {
            return position_0 // 
                    + "            DB _db = " + _result + ";\n" // 
                    + "            return new DBWrapper(this.getFilters(), _db, _db.getMongo(), _db.getName());\n";
        }

        if (method.getReturnType().equals(DBCursor.class)) {
            return position_0 // 
                    + "            DBCursor _dbCursor = " + _result + ";\n" // 
                    + "            return new DBCursorWrapper(this.getFilters(), _dbCursor, _dbCursor.getCollection(), null, null, null);\n";
        }

        //        if (method.getReturnType().equals(DBObject.class)) {
        //            return position_0 // 
        //                    + "            DBObject _dbObject = " + _result + ";\n" // 
        //                    + "            return new DBObjectWrapper(this, _dbObject);\n";
        //        }

        if (method.getReturnType().equals(MongoClient.class)) {
            return position_0 //
                    + "            return new MongoClientWrapper(this.getFilters(), " + _result + ");\n";
        }

        if (method.getReturnType().equals(MongoDatabase.class)) {
            return position_0 //  
                    + "            return new MongoDatabaseWrapper(this.getFilters(), " + _result + ");\n";
        }

        if (method.getReturnType().equals(MongoCollection.class)) {
            return position_0 //  
                    + "            return new MongoCollectionWrapper(this.getFilters(), " + _result + ");\n";
        }

        return position_0 //  
                + "    " + _return(method) + " " + _result + ";\n";//
    }

    protected String args(Method method) {
        List<String> args = new ArrayList<String>();
        for (int i = 0; i < method.getParameterCount(); i++) {
            args.add(" $" + i);
        }
        return StringUtil.join(args);
    }

    protected String args_2(Method method) {
        List<String> args = new ArrayList<String>();
        for (int i = 0; i < method.getParameterCount(); i++) {
            args.add(" $" + i);
        }
        return args.isEmpty() ? "" : ", " + StringUtil.join(args);
    }

    protected String parameters(Method method) {
        if ("public com.mongodb.WriteResult com.mongodb.DBCollection.insert(com.mongodb.DBObject[])".equals(method.toString())) {
            return "DBObject... $0";
        }

        if ("public com.mongodb.AggregationOutput com.mongodb.DBCollection.aggregate(com.mongodb.DBObject,com.mongodb.DBObject[])".equals(method.toString())) {
            return "DBObject $0,DBObject... $1";
        }

        if ("public com.mongodb.WriteResult com.mongodb.DBCollection.insert(com.mongodb.WriteConcern,com.mongodb.DBObject[])".equals(method.toString())) {
            return "WriteConcern $0,DBObject... $1";
        }

        if ("public com.mongodb.CommandResult com.mongodb.DB.doEval(java.lang.String,java.lang.Object[])".equals(method.toString())) {
            return "String $0,Object... $1";
        }

        if ("public java.lang.Object com.mongodb.DB.eval(java.lang.String,java.lang.Object[])".equals(method.toString())) {
            return "String $0,Object... $1";
        }

        List<String> parameters = new ArrayList<String>();
        Integer index = 0;
        for (Class<?> type : method.getParameterTypes()) {
            parameters.add(type.getSimpleName() + " $" + index);
            index++;
        }

        return StringUtil.join(parameters);
    }

    protected String parameters_2(Method method) {
        List<String> parameters = new ArrayList<String>();
        Integer index = 0;
        for (Class<?> type : method.getParameterTypes()) {
            parameters.add(type.getSimpleName() + " $" + index);
            index++;
        }

        return parameters.isEmpty() ? "" : ", " + StringUtil.join(parameters);
    }
}
